//
//  NetworkClient.swift
//  SitPosition
//
//  Created by apple on 2019/1/6.
//  Copyright © 2019 apple. All rights reserved.
//

import Foundation
import Alamofire

// Token失效通知
let AccessTokenInvalid: Notification.Name = Notification.Name("AccessTokenInvalid")

public struct NetworkClient {
    
    // 默认的客户端
    public static var instance: NetworkClient = NetworkClient(ServerURLString)
    
    // 客户端认证相关配置
    public static let clientId = "oauth_ios"
    public static let clientSecret = "huimouzhineng"
    public static let guestAccessToken = "68e65bcd-5d84-49da-b3b7-e1c2bfc7c496"
    
    public static var clientInfo: String {
        #if AUTOGEN
        let s = "\(clientId):\(clientSecret)"
        guard let data = s.data(using: String.Encoding.utf8, allowLossyConversion: true) else {
            fatalError()
        }
        
        let options = Data.Base64EncodingOptions(rawValue: 0)
        let base64String = data.base64EncodedString(options: options)
        return "Basic \(base64String)"
        #else
        return "Basic b2F1dGhfaW9zOmh1aW1vdXpoaW5lbmc="
        #endif
    }
    
    // access_token
    public var accessToken: String? = nil
    
    // 公共Headers
    private let commonHeaders: HTTPHeaders = {
        let appVersion: String = Bundle.main.releaseVersionNumber ?? "1.0"
        let deviceModel: String = UIDevice.current.model
        let systemVersion: String = UIDevice.current.systemVersion
        return [
            "os" : "ios",
            "osVersion" : systemVersion,
            "device" : deviceModel,
            "appVersion": appVersion
        ]
    }()
    
    // 基URL
    let _baseURL: String
    // 会话管理器
    let _sessionManager: SessionManager = SessionManager()
    
    // MARK: - Object lifecycle
    
    public init(_ baseURL: String) {
        _baseURL = baseURL
        
        let handler = OAuth2Handler(baseURLString: ServerURLString,
                                    Authorization: NetworkClient.clientInfo,
                                    guestToken: NetworkClient.guestAccessToken)
        _sessionManager.adapter = handler
        _sessionManager.retrier = handler
    }
    
    // 通用Data请求完成回调
    public typealias DataRequestCompletion = (DataResponse<Data>) -> Void
    public func commonDataRequestCompletion(
        success: DataParamCallback?,
        failure: ErrorCallback?) -> DataRequestCompletion {
        return { response in
            switch response.result {
            case .success(let data):
                success?(data)
            case .failure(let error):
                guard let data_ = response.data,
                    let error_ = SPError.init(data: data_) else {
                        failure?(error)
                        return
                }
                failure?(error_)
                if error_.isTokenExpiredError {
                    NotificationCenter.default.post(name: AccessTokenInvalid, object: self)
                }
            }
        }
    }
    
    // 根据接口的相对路径获取接口的URL
    func api(_ path: String?) -> String {
        guard let baseURL: URL = URL(string: _baseURL) else {
            return _baseURL
        }
        guard let path_ = path else {
            return _baseURL
        }
        guard let url: NSURL = NSURL(string: path_, relativeTo: baseURL) else {
            return _baseURL
        }
        return url.absoluteString ?? _baseURL
    }
    
    @discardableResult
    public func get(_ path: String, params: Parameters? = nil, headers: HTTPHeaders? = nil) -> DataRequest {
        return _sessionManager.request(api(path),
                                 method: .get,
                                 parameters: params,
                                 encoding: URLEncoding.default,
                                 headers: headers)
    }
    
    @discardableResult
    public func post(_ path: String, params: Parameters? = nil, headers: HTTPHeaders? = nil) -> DataRequest {
        return _sessionManager.request(api(path),
                                 method: .post,
                                 parameters: params,
                                 encoding: JSONEncoding.default,
                                 headers: headers)
    }
    
    @discardableResult
    public func patch(_ path: String, params: Parameters? = nil, headers: HTTPHeaders? = nil) -> DataRequest {
        return _sessionManager.request(api(path),
                                 method: .patch,
                                 parameters: params,
                                 encoding: JSONEncoding.default,
                                 headers: headers)
    }
    
    @discardableResult
    public func delete(_ path: String, params: Parameters? = nil, headers: HTTPHeaders? = nil) -> DataRequest {
        return _sessionManager.request(api(path),
                                 method: .delete,
                                 parameters: params,
                                 encoding: URLEncoding.default,
                                 headers: headers)
    }
    
    // 对NetworkApi的支持
    @discardableResult
    public func request(_ api: NetworkApi, params: Parameters? = nil, headers: HTTPHeaders? = nil) -> DataRequest {
        var result: DataRequest!
        
        // Path + access_token
        let path: String = api.path.URLByAppendingQueryItems(
            ["access_token" : accessToken ?? NetworkClient.guestAccessToken]
        )
        
        // Headers
        var headers_: HTTPHeaders = headers ?? HTTPHeaders()
        for (k, v) in commonHeaders {
            headers_[k] = v;
        }
        
        // Request
        switch api.method {
        case .get:
            result = get(path, params: params, headers: headers_)
        case .post:
            result = post(path, params: params, headers: headers_)
        case .patch:
            result = patch(path, params: params, headers: headers_)
        case .delete:
            result = delete(path, params: params, headers: headers_)
        default:
            fatalError("Not implemented yet")
        }
        return result
            .validate(statusCode: 200..<300)
            .validate(contentType: ["application/json"])
    }
    
}
